﻿using System;
using System.Collections.Generic;
using System.Text;
using System.Configuration;
using System.IO;
using System.Net;
using System.Linq;
using System.Windows.Forms;

namespace CSI300Cons.CapitalDownloader
{
    public class DownloaderTask : ITask
    {
        #region Members

        private const string CapUrl = "http://finance.sina.com.cn/realstock/company/{0}{1}/nc.shtml";

        private static string ListPath = ConfigurationManager.AppSettings["ListPath"];
        private static string DBPath = ConfigurationManager.AppSettings["DBPath"];
        private static int WorkHour = Convert.ToInt32(ConfigurationManager.AppSettings["WorkHour"]);
        private static int OnlyWorkHour = Convert.ToInt32(ConfigurationManager.AppSettings["OnlyWorkHour"]);
        private static List<KeyValuePair<int, string>> NameList;

        private static DateTime[] schedlist = { DateTime.Today.AddHours(9).AddMinutes(35),
                                                  DateTime.Today.AddHours(10).AddMinutes(5), 
                                                  DateTime.Today.AddHours(10).AddMinutes(35), 
                                                  DateTime.Today.AddHours(11).AddMinutes(5), 
                                                  DateTime.Today.AddHours(11).AddMinutes(35), 
                                                  DateTime.Today.AddHours(13).AddMinutes(5), 
                                                  DateTime.Today.AddHours(13).AddMinutes(35), 
                                                  DateTime.Today.AddHours(14).AddMinutes(5), 
                                                  DateTime.Today.AddHours(14).AddMinutes(35), 
                                                  DateTime.Today.AddHours(15).AddMinutes(5) };

        private static readonly object obj = new object();
        static int temp;

        #endregion

        #region Properities

        private CompositeIndex firstCI;

        private static bool needSavePrice;

        #endregion

        #region ctor.

        static DownloaderTask()
        {
            LanceCarol.Utility.DbHelperOleDb.UpdateConnectionString("Provider=Microsoft.Jet.OLEDB.4.0;Data Source=|DataDirectory|" + DBPath + ";Persist Security Info=True");
        }

        #endregion

        #region Business Logical

        public static void GetCurrentCapital(List<PriceRecord> list, bool needSave)
        {
            string result;

            foreach (PriceRecord p in list)
            {
                using (WebClient wc = new WebClient())
                {
                    using (Stream resStream = wc.OpenRead(string.Format(CapUrl, p.StockID < 600000 ? "sz" : "sh", p.StockID.ToString("000000"))))
                    {
                        StreamReader sr = new StreamReader(resStream, System.Text.Encoding.Default);

                        while (sr.Peek() >= 0)
                        {
                            result = sr.ReadLine();
                            if (result.StartsWith("var totalcapital"))
                            {
                                int start = result.IndexOf('=');
                                p.TotalCapital = Convert.ToDouble(result.Substring(start + 1, result.IndexOf(';') - start - 1));
                                p.TotalValue = p.TotalCapital * p.CurrentPrice;

                                result = sr.ReadLine();
                                start = result.IndexOf('=');
                                p.CurrentCapital = Convert.ToDouble(result.Substring(start + 1, result.IndexOf(';') - start - 1));
                                if (p.CurrentCapital == 0)
                                {
                                    result = sr.ReadLine();
                                    start = result.IndexOf('=');
                                    p.CurrentCapital = Convert.ToDouble(result.Substring(start + 1, result.IndexOf(';') - start - 1));
                                }

                                //Program.Logger.Output(string.Format("{0}{1}  当前价：{2} 总股本：{3} 总市值：{4} 流通股本：{5}", p.StockID.ToString("000000"), p.StockName, p.CurrentPrice, p.TotalCapital, p.TotalValue, p.CurrentCapital), ConsoleColor.Green);

                                if (needSave)
                                {
                                    p.Save();
                                }
                                break;
                            }
                        }
                    }
                }
            }
        }

        //private static void GetCurrentCapital(List<PriceRecord> list, bool needSave)
        //{
        //    System.Diagnostics.Stopwatch sw = System.Diagnostics.Stopwatch.StartNew();
        //    foreach (PriceRecord p in list)
        //    {
        //        using (WebClient wc = new WebClient())
        //        {
        //            wc.OpenReadAsync(new Uri(string.Format(CapUrl, p.StockID < 600000 ? "sz" : "sh", p.StockID.ToString("000000"))), p);
        //            wc.OpenReadCompleted += new OpenReadCompletedEventHandler(wc_OpenReadCompleted);
        //        }
        //    }
            
        //    temp = 0;
        //    while (temp != 300)
        //    {
        //        System.Threading.Thread.Sleep(50);
        //    }

        //    System.Diagnostics.Debug.WriteLine(sw.ElapsedMilliseconds.ToString());
        //    return;
        //}

        //static void wc_OpenReadCompleted(object sender, OpenReadCompletedEventArgs e)
        //{
        //    string result;
        //    PriceRecord p = (PriceRecord)e.UserState;
        //    using (Stream resStream = e.Result)
        //    {
        //        StreamReader sr = new StreamReader(resStream, System.Text.Encoding.Default);

        //        while (sr.Peek() >= 0)
        //        {
        //            result = sr.ReadLine();
        //            if (result.StartsWith("var totalcapital"))
        //            {
        //                int start = result.IndexOf('=');
        //                p.TotalCapital = Convert.ToDouble(result.Substring(start + 1, result.IndexOf(';') - start - 1));
        //                p.TotalValue = p.TotalCapital * p.CurrentPrice;

        //                result = sr.ReadLine();
        //                start = result.IndexOf('=');
        //                p.CurrentCapital = Convert.ToDouble(result.Substring(start + 1, result.IndexOf(';') - start - 1));
        //                if (p.CurrentCapital == 0)
        //                {
        //                    result = sr.ReadLine();
        //                    start = result.IndexOf('=');
        //                    p.CurrentCapital = Convert.ToDouble(result.Substring(start + 1, result.IndexOf(';') - start - 1));
        //                }

        //                if (needSavePrice)
        //                {
        //                    p.Save();
        //                    Program.Logger.Output(string.Format("{0}{1}  当前价：{2} 总股本：{3} 总市值：{4} 流通股本：{5}", p.StockID.ToString("000000"), p.StockName, p.CurrentPrice, p.TotalCapital, p.TotalValue, p.CurrentCapital), ConsoleColor.Green, true);
        //                }

        //                lock (obj)
        //                {
        //                    temp++;
        //                }

        //                break;
        //            }
        //        }
        //    }
        //}

        public static List<KeyValuePair<int, string>> LoadNameList()
        {
            NameList = new List<KeyValuePair<int, string>>();

            using (StreamReader vStreamReader = new StreamReader(Application.StartupPath + ListPath, Encoding.Default))
            {
                string vLine;
                while ((vLine = vStreamReader.ReadLine()) != null)
                {
                    NameList.Add(new KeyValuePair<int, string>(
                        Convert.ToInt32(vLine.Substring(0, 6)),
                        vLine.Substring(7).Trim()));
                }
            }

            return NameList;
        }

        public static List<PriceRecord> GetCurrentPrice(List<KeyValuePair<int, string>> NameList)
        {
            List<PriceRecord> recordList = new List<PriceRecord>();

            StringBuilder results = new StringBuilder();

            for (int j = 0; j < 3; j++)
            {
                StringBuilder url = new StringBuilder("http://hq.sinajs.cn/list=");

                for (int i = 0; i < 100; i++)
                {
                    KeyValuePair<int, string> stock = NameList[j * 100 + i];

                    if (stock.Key < 600000)
                    {
                        url.AppendFormat("{0}{1}", "sz", stock.Key.ToString("000000"));
                    }
                    else
                    {
                        url.AppendFormat("{0}{1}", "sh", stock.Key.ToString("000000"));
                    }
                    url.Append(",");
                }

                using (WebClient wc = new WebClient())
                {
                    using (Stream resStream = wc.OpenRead(url.ToString()))
                    {
                        StreamReader sr = new StreamReader(resStream, System.Text.Encoding.Default);
                        results.Append(sr.ReadToEnd());
                    }
                }
            }

            string[] resultList = results.ToString().Split(';');

            PriceRecord record;
            char[] sp = { '0', '6' };
            foreach (string r in resultList)
            {
                if (r.Length < 30)
                    continue;
                record = new PriceRecord();
                record.StockID = Convert.ToInt32(r.Substring(r.IndexOfAny(sp), 6));
                int start = r.IndexOf('"');
                record.StockName = r.Substring(start + 1, r.IndexOf(',') - start - 1);
                start = r.IndexOf(',', r.IndexOf(',', r.IndexOf(',') + 1) + 1);
                record.CurrentPrice = Convert.ToDouble(r.Substring(start + 1, r.IndexOf(',', start + 1) - start - 1));

                if (record.CurrentPrice == 0)
                {
                    start = r.IndexOf(',', r.IndexOf(',') + 1);
                    record.CurrentPrice = Convert.ToDouble(r.Substring(start + 1, r.IndexOf(',', start + 1) - start - 1));
                }

                record.DownloadDate = DateTime.Today.Date;

                recordList.Add(record);
            }

            return recordList;
        }

        /// <summary>
        /// 获得昨日收盘价
        /// </summary>
        /// <param name="NameList"></param>
        /// <returns></returns>
        private static List<PriceRecord> GetYesterClosePrice(List<KeyValuePair<int, string>> NameList)
        {
            List<PriceRecord> recordList = new List<PriceRecord>();

            StringBuilder results = new StringBuilder();

            for (int j = 0; j < 3; j++)
            {
                StringBuilder url = new StringBuilder("http://hq.sinajs.cn/list=");

                for (int i = 0; i < 100; i++)
                {
                    KeyValuePair<int, string> stock = NameList[j * 100 + i];

                    if (stock.Key < 600000)
                    {
                        url.AppendFormat("{0}{1}", "sz", stock.Key.ToString("000000"));
                    }
                    else
                    {
                        url.AppendFormat("{0}{1}", "sh", stock.Key.ToString("000000"));
                    }
                    url.Append(",");
                }

                using (WebClient wc = new WebClient())
                {
                    using (Stream resStream = wc.OpenRead(url.ToString()))
                    {
                        StreamReader sr = new StreamReader(resStream, System.Text.Encoding.Default);
                        results.Append(sr.ReadToEnd());
                    }
                }
            }

            string[] resultList = results.ToString().Split(';');

            PriceRecord record;
            char[] sp = { '0', '6' };
            foreach (string r in resultList)
            {
                if (r.Length < 30)
                    continue;
                record = new PriceRecord();
                record.StockID = Convert.ToInt32(r.Substring(r.IndexOfAny(sp), 6));
                int start = r.IndexOf('"');
                record.StockName = r.Substring(start + 1, r.IndexOf(',') - start - 1);

                start = r.IndexOf(',', r.IndexOf(',', r.IndexOf(',') + 1) + 1);
                record.CurrentPrice = Convert.ToDouble(r.Substring(start + 1, r.IndexOf(',', start + 1) - start - 1));

                if (record.CurrentPrice == 0)
                {
                    start = r.IndexOf(',', r.IndexOf(',') + 1);
                    record.CurrentPrice = Convert.ToDouble(r.Substring(start + 1, r.IndexOf(',', start + 1) - start - 1));
                }

                record.DownloadDate = DateTime.Today.Date;

                recordList.Add(record);
            }

            return recordList;
        }

        private static double GetDownloadBI()
        {
            Program.Logger.Output("开始获取当前沪深300的价格...", ConsoleColor.Blue, true);
            string url = "http://hq.sinajs.cn/?list=s_sh000300";
            double price;
            using (WebClient wc = new WebClient())
            {
                using (Stream resStream = wc.OpenRead(url.ToString()))
                {
                    StreamReader sr = new StreamReader(resStream, System.Text.Encoding.Default);

                    string content = sr.ReadToEnd();
                    int start = content.IndexOf(',');
                    int end = content.Substring(start + 1).IndexOf(',');
                    price = Convert.ToDouble(content.Substring(start + 1, end));
                    Program.Logger.Output(string.Format("当前沪深300的价格是：{0}", price.ToString()), ConsoleColor.Green, true);
                }
            }
            return price;
        }

        private static double GetRadixCapital(List<PriceRecord> list)
        {
            double radix = 0;

            Program.Logger.Output("开始计算基数市值...", ConsoleColor.Blue, true);

            list.ForEach(p => radix += (p.AdjustedCapital * p.CurrentPrice));

            Program.Logger.Output(string.Format("当前基数市值是：{0}", radix), ConsoleColor.Green, true);

            return radix;
        }

        private static double GetCalculatedBI(double currentRadixCapital,double firstRadixCapital,double firstDownloadBI)
        {
            return currentRadixCapital / firstRadixCapital * firstDownloadBI;
        }

        private static bool needGetBI()
        {
            DateTime dt = DateTime.Now;

            foreach (DateTime item in schedlist)
            {
                if (item.Hour == dt.Hour && item.Minute == dt.Minute)
                {
                    return true;
                }
            }
            return false;
        }

        #endregion

        #region ITask Members

        public void MainTaskExecute()
        {
            //DailyDownload();
            //BIDownload();
            try
            {
                //ZuchengQuanzhong();
                CodeQuanzhongPrice();
            }
            catch (Exception)
            {
                System.Threading.Thread.Sleep(10000);
                try
                {
                    ZuchengQuanzhong();
                }
                catch (Exception)
                {
                    System.Threading.Thread.Sleep(10000);
                    try
                    {
                        ZuchengQuanzhong();
                    }
                    catch (Exception)
                    {
                        System.Threading.Thread.Sleep(10000);
                        ZuchengQuanzhong();
                    }
                }
            }
        }

        private void CodeQuanzhongPrice()
        {
            //代码|调整股本|价格

            lock (this)
            {
                LoadNameList();
                List<PriceRecord> list = GetCurrentPrice(NameList);
                GetCurrentCapital(list, false);

                list = list.OrderBy(p => p.AdjustedCapital * p.CurrentPrice).ToList();

                StringBuilder sb = new StringBuilder();

                foreach (var p in list)
                {
                    sb.AppendLine(string.Format("{0}|{1}|{2}", p.StockID.ToString("000000"), p.AdjustedCapital, p.CurrentPrice));
                }
                sb.AppendLine("下载结束");

                this.WriteToFile(Path.GetDirectoryName(Application.ExecutablePath) + @"\Data\HS300i" + DateTime.Now.ToString("yyyyMMdd") + ".txt", sb.ToString());

                MailHelper.SendMailUseSMTP("Just test the Downloader", sb.ToString().Replace("\r\n", "<br/>"));
            }
        }

        protected void DailyDownload()
        {
            if (OnlyWorkHour == 1 && (DateTime.Now.Hour != WorkHour || DateTime.Now.Minute != 0))
            {
#if debug
                Program.Logger.Output(string.Format("当前设置的下载时间是每天{0}:00-{1}:00", WorkHour, WorkHour + 1), ConsoleColor.Yellow);
                Program.Logger.Output("现在不是下载时间，程序将休眠以等待下次执行", ConsoleColor.Yellow)
#endif
                return;
            }
            PriceRecord.ClearToday();
            needSavePrice = true;
            Program.Logger.Output("开始重新下载今日数据", ConsoleColor.Blue);

            LoadNameList();

            List<PriceRecord> list = GetCurrentPrice(NameList);

            bool needSave = true;
            GetCurrentCapital(list, needSave);
            Program.Logger.Output("数据已全部下载完成", ConsoleColor.Blue);
            needSavePrice = false;
        }

        protected void BIDownload()
        {
            if (!needGetBI())
                return;

            Program.Logger.Output("开始下载指数数据", ConsoleColor.Blue, true);

            LoadNameList();
            List<PriceRecord> list = GetCurrentPrice(NameList);

            bool needSave = false;
            GetCurrentCapital(list, needSave);

            if (DateTime.Now.Hour == 9)
            {
                firstCI = new CompositeIndex();
                firstCI.DownloadBI = GetDownloadBI();
                firstCI.RadixCapital = GetRadixCapital(list);
                firstCI.CalculatedBI = GetCalculatedBI(firstCI.RadixCapital, firstCI.RadixCapital, firstCI.DownloadBI);
                firstCI.DownloadTime = new DateTime();
                firstCI.DownloadTime = DateTime.Now;
                Program.Logger.Output(string.Format("计算指数：{0} 下载指数{1}", firstCI.CalculatedBI, firstCI.DownloadBI), ConsoleColor.Green, true);
                firstCI.Save();
            }
            else
            {
                if (firstCI == null || firstCI.DownloadTime.Date != DateTime.Today.Date)
                {
                    firstCI = CompositeIndex.LoadFirstCI(DateTime.Now);
                    Program.Logger.Output("Fist CI loaded", ConsoleColor.Blue, true);
                }

                CompositeIndex newCI = new CompositeIndex();
                newCI.DownloadBI = GetDownloadBI();
                newCI.RadixCapital = GetRadixCapital(list);
                newCI.CalculatedBI = GetCalculatedBI(newCI.RadixCapital, firstCI.RadixCapital, firstCI.DownloadBI);
                newCI.DownloadTime = DateTime.Now;
                Program.Logger.Output(string.Format("计算指数：{0} 下载指数{1}", newCI.CalculatedBI, newCI.DownloadBI), ConsoleColor.Green, true);
                newCI.Save();
            }
        }

        protected void ZuchengQuanzhong()
        {
            if (DateTime.Now.Hour != 9 || DateTime.Now.Minute != 35)
            {
                return;
            }
            lock (this)
            {
                LoadNameList();
                List<PriceRecord> list = GetCurrentPrice(NameList);
                GetCurrentCapital(list, false);

                double totalAdjustCapital = list.Sum(p => p.AdjustedCapital);

                list = list.OrderByDescending(p => p.AdjustedCapital * p.CurrentPrice).ToList();

                StringBuilder sb = new StringBuilder();
                double test = 0;

                //foreach (var p in list)
                //{
                //    Program.Logger.Output(string.Format("{0}{1} 权重：{2} 调整市值：{3}", p.StockID.ToString("000000"), p.StockName, p.AdjustedCapital / totalAdjustCapital, p.AdjustedCapital * p.CurrentPrice), ConsoleColor.Green);
                //    sb.AppendFormat("{0}|{1}|{2}\r\n", p.StockID.ToString("000000"), p.AdjustedCapital / totalAdjustCapital, p.AdjustedCapital * p.CurrentPrice);
                //    test += p.AdjustedCapital / totalAdjustCapital;
                //}
                //sb.AppendLine("下载结束" + test.ToString());
                //this.WriteToFile(Path.GetDirectoryName(Application.ExecutablePath) + @"\Data\HS300i" + DateTime.Now.ToString("yyyyMMdd") + ".txt", sb.ToString());

                firstCI = new CompositeIndex();
                firstCI.DownloadBI = GetDownloadBI();
                firstCI.RadixCapital = GetRadixCapital(list);

                sb = new StringBuilder();

                test = 0;

                foreach (var p in list)
                {
                    Program.Logger.Output(string.Format("{0}{1} 权重：{2} 调整市值：{3}", p.StockID.ToString("000000"), p.StockName, Math.Round(Convert.ToDouble(p.AdjustedCapital * firstCI.DownloadBI * 300 / firstCI.RadixCapital)), p.AdjustedCapital * p.CurrentPrice), ConsoleColor.Green);
                    sb.AppendLine(string.Format("{0}|{1}|{2}", p.StockID.ToString("000000"),  Math.Round(Convert.ToDouble(p.AdjustedCapital * firstCI.DownloadBI * 300 / firstCI.RadixCapital)), p.AdjustedCapital * p.CurrentPrice));
                    test += Convert.ToDouble(p.AdjustedCapital * firstCI.DownloadBI * 300 / firstCI.RadixCapital);
                }
                sb.AppendLine("下载结束" + test.ToString(".00"));


                this.WriteToFile(Path.GetDirectoryName(Application.ExecutablePath) + @"\Data\HS300i" + DateTime.Now.ToString("yyyyMMdd") + ".txt", sb.ToString());

                MailHelper.SendMailUseSMTP("Just test the Downloader", sb.ToString().Replace("\r\n", "<br/>"));
            }

            System.Threading.Thread.Sleep(10000);
        }

        #endregion

        private void WriteToFile(string filename, string content)
        {
            using (StreamWriter sw = new StreamWriter(filename, true))
            {
                sw.WriteLine(content);
                sw.Close();
            }
        }
    }
}
